/*
Copyright (C) 1999 CERN - European Organization for Nuclear Research.
Permission to use, copy, modify, distribute and sell this software and its documentation for any purpose 
is hereby granted without fee, provided that the above copyright notice appear in all copies and 
that both that copyright notice and this permission notice appear in supporting documentation. 
CERN makes no representations about the suitability of this software for any purpose. 
It is provided "as is" without expressed or implied warranty.
 */
package cern.jet.random;

import cern.colt.list.tdouble.DoubleArrayList;
import cern.colt.list.tint.IntArrayList;
import cern.jet.random.tdouble.AbstractDoubleDistribution;
import cern.jet.random.tdouble.Poisson;
import cern.jet.random.tdouble.engine.DoubleRandomEngine;

/**
 * Benchmarks random number generation from various distributions as well as PDF
 * and CDF lookups.
 * 
 * @author wolfgang.hoschek@cern.ch
 * @version 1.0, 09/24/99
 */
public class Benchmark extends cern.colt.PersistentObject {
    /**
     * 
     */
    private static final long serialVersionUID = -8509762371822439740L;
    protected DoubleRandomEngine randomGenerator;

    /**
     * Makes this class non instantiable, but still let's others inherit from
     * it.
     */
    protected Benchmark() {
        throw new RuntimeException("Non instantiable");
    }

    /**
     * Prints the first <tt>size</tt> random numbers generated by the
     * distribution.
     */
    public static void demo1() {
        // Gamma distribution

        // define distribution parameters
        double mean = 5;
        double variance = 1.5;
        double alpha = mean * mean / variance;
        double lambda = 1 / (variance / mean);

        // for tests and debugging use a random engine with CONSTANT seed -->
        // deterministic and reproducible results
        cern.jet.random.tdouble.engine.DoubleRandomEngine engine = new cern.jet.random.tdouble.engine.DoubleMersenneTwister();

        // your favourite distribution goes here
        cern.jet.random.tdouble.AbstractDoubleDistribution dist = new cern.jet.random.tdouble.Gamma(alpha, lambda,
                engine);

        // collect random numbers and print statistics
        int size = 100000;
        cern.colt.list.tdouble.DoubleArrayList numbers = new cern.colt.list.tdouble.DoubleArrayList(size);
        for (int i = 0; i < size; i++)
            numbers.add(dist.nextDouble());

        hep.aida.tdouble.bin.DynamicDoubleBin1D bin = new hep.aida.tdouble.bin.DynamicDoubleBin1D();
        bin.addAllOf(numbers);
        System.out.println(bin);
    }

    /**
     * Benchmarks all subclasses
     * 
     * @param args
     *            args[0] - the number of random numbers to be generated per
     *            subclass. args[1] - <tt>true</tt> prints each generated
     *            number, <tt>false</tt> does not print generated numbers (use
     *            this setting for benchmarking).
     */
    public static void main(String[] args) {
        int size = Integer.parseInt(args[0]);
        boolean print = new Boolean(args[1]).booleanValue();
        double mean = new Double(args[2]).doubleValue();
        String generatorName = args[3];
        random(size, print, mean, generatorName);
    }

    /**
     * Benchmarks all subclasses
     * 
     * @param size
     *            the number of random numbers to be generated per subclass.
     * @param print
     *            <tt>true</tt> prints each generated number, <tt>false</tt>
     *            does not print generated numbers (use this setting for
     *            benchmarking).
     * @param mean
     *            the mean for distributions that require a mean.
     */
    public static void random(int size, boolean print, double mean, String generatorName) {
        System.out.println("Generating " + size + " random numbers per distribution...\n");

        // int large = 100000000;
        DoubleRandomEngine gen; // = new MersenneTwister();
        try {
            gen = (DoubleRandomEngine) Class.forName(generatorName).newInstance();
        } catch (Exception exc) {
            throw new InternalError(exc.getMessage());
        }

        /*
         * randomInstance(size,print,new Zeta(10.0,
         * 10.0,(RandomEngine)gen.clone())); randomInstance(size,print,new
         * Zeta(1.0, 1.0, (RandomEngine)gen.clone()));
         * randomInstance(size,print,new Zeta(mean, mean,
         * (RandomEngine)gen.clone())); randomInstance(size,print,new Zeta(mean,
         * 1/mean, (RandomEngine)gen.clone())); //randomInstance(size,print,new
         * Zeta(1/mean, mean, (RandomEngine)gen.clone()));
         */

        /*
         * 
         * randomInstance(size,print,new Beta(10.0,
         * 10.0,(RandomEngine)gen.clone())); randomInstance(size,print,new
         * Beta(1.0, 1.0, (RandomEngine)gen.clone()));
         * randomInstance(size,print,new Beta(mean, mean,
         * (RandomEngine)gen.clone())); randomInstance(size,print,new Beta(mean,
         * 1/mean, (RandomEngine)gen.clone())); randomInstance(size,print,new
         * Beta(1/mean, mean, (RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new Uniform((RandomEngine)gen.clone()));
         */
        randomInstance(size, print, new Poisson(mean, (DoubleRandomEngine) gen.clone()));
        /*
         * randomInstance(size,print,new
         * PoissonSlow(mean,(RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new
         * Poisson(3.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * PoissonSlow(3.0,(RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new
         * Binomial(1,0.5,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Binomial(5,0.3,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Binomial((int)mean,0.999999999,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Binomial((int)mean,1.0/mean,(RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new
         * Exponential(1.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Exponential(3.0,(RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new
         * Normal(0.0,1.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Normal(3.0,1.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Normal(mean,largeVariance,(RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new BreitWigner(1.0, 0.2,
         * Double.NEGATIVE_INFINITY, (RandomEngine)gen.clone()));
         * randomInstance(size,print,new BreitWigner(1.0, 0.2, 1.0,
         * (RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new BreitWignerMeanSquare(1.0, 0.2,
         * Double.NEGATIVE_INFINITY, (RandomEngine)gen.clone()));
         * randomInstance(size,print,new BreitWignerMeanSquare(1.0, 0.2, 1.0,
         * (RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new
         * ChiSquare(1.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * ChiSquare(5.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * ChiSquare(mean,(RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new
         * Gamma(0.2,1.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Gamma(1.0,1.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Gamma(3.0,0.5,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Gamma(mean,0.5,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Gamma(mean,1.0/mean,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * Gamma(mean,mean,(RandomEngine)gen.clone()));
         * 
         * randomInstance(size,print,new
         * StudentT(1.0,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * StudentT(2.5,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * StudentT(mean,(RandomEngine)gen.clone()));
         * randomInstance(size,print,new
         * StudentT(1.0/mean,(RandomEngine)gen.clone()));
         * 
         * int probs = 10000; double[] pdf = new double[probs]; for (int i=0; i<probs;
         * i++) pdf[i]=i*i; // prepare f(x)=x^2 distrib.
         * randomInstance(size,print,new
         * Empirical(pdf,Empirical.NO_INTERPOLATION,
         * (RandomEngine)gen.clone())); randomInstance(size,print,new
         * Empirical(pdf,Empirical.LINEAR_INTERPOLATION,
         * (RandomEngine)gen.clone()));
         */
    }

    /**
     * generates <size> random numbers from <dist>
     */
    public static void randomInstance(int size, boolean print, AbstractDoubleDistribution dist) {
        System.out.print("\n" + dist + " ...");
        cern.colt.Timer timer = new cern.colt.Timer().start();

        for (int i = size; --i >= 0;) {
            double rand = dist.nextDouble();
            if (print) {
                if ((size - i - 1) % 8 == 0)
                    System.out.println();
                System.out.print((float) rand + ", ");
            }
        }

        timer.stop();
        System.out.println("\n" + timer);
    }

    /**
     * Prints the first <tt>size</tt> random numbers generated by the
     * distribution.
     */
    public static void test(int size, AbstractDoubleDistribution distribution) {
        for (int j = 0, i = size; --i >= 0; j++) {
            System.out.print(" " + distribution.nextDouble());
            if (j % 8 == 7)
                System.out.println();
        }
        System.out.println("\n\nGood bye.\n");
    }

    /**
     * Prints the first <tt>size</tt> random numbers generated by the
     * distribution.
     */
    public static void test2(int size, AbstractDoubleDistribution distribution) {
        hep.aida.tdouble.bin.DynamicDoubleBin1D bin = new hep.aida.tdouble.bin.DynamicDoubleBin1D();
        for (int j = 0, i = size; --i >= 0; j++) {
            bin.add(distribution.nextDouble());
        }
        System.out.println(bin);
        System.out.println("\n\nGood bye.\n");
    }

    /**
     * Prints the first <tt>size</tt> random numbers generated by the
     * distribution.
     */
    public static void test2(int size, AbstractDoubleDistribution a, AbstractDoubleDistribution b) {
        hep.aida.tdouble.bin.DynamicDoubleBin1D binA = new hep.aida.tdouble.bin.DynamicDoubleBin1D();
        hep.aida.tdouble.bin.DynamicDoubleBin1D binB = new hep.aida.tdouble.bin.DynamicDoubleBin1D();
        for (int j = 0, i = size; --i >= 0; j++) {
            binA.add(a.nextDouble());
            binB.add(b.nextDouble());
        }
        // System.out.println(binA);
        // System.out.println(binB);
        // System.out.println(binA.compareWith(binB));

        System.out.println("\n\nBenchmarking frequencies...\n");
        IntArrayList freq = new IntArrayList();
        DoubleArrayList distinct = new DoubleArrayList();
        cern.colt.Timer timer = new cern.colt.Timer();
        timer.reset();
        timer.start();
        binA.frequencies(distinct, freq);
        timer.stop().display();
        // System.out.println(distinct);
        // System.out.println(freq);

        /*
         * timer.reset(); timer.start(); binA.xfrequencies2(distinct,freq);
         * timer.stop().display(); //System.out.println(distinct);
         * //System.out.println(freq);
         */

        /*
         * distinct.shuffle(); timer.reset().start(); distinct.sort();
         * timer.stop().display();
         * 
         * timer.reset().start(); binA.frequencies(distinct,freq);
         * timer.stop().display(); //System.out.println(distinct);
         * //System.out.println(freq);
         */

        System.out.println("\n\nGood bye.\n");
    }
}
